package com.bosssoft.controller;

import com.bosssoft.entity.po.FibVoucherDetailsPO;
import com.bosssoft.entity.vo.*;
import com.bosssoft.exception.DaoException;
import com.bosssoft.response.Result;
import com.bosssoft.service.FibAccountService;
import com.bosssoft.service.FibBudgetFeignService;
import com.bosssoft.service.FibSealFeignService;
import com.bosssoft.service.VoucherFeignService;
import com.bosssoft.utils.MyClassCastUtil;
import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;

/**
 * @author 项昊天
 * @create 2021/8/23 15:10
 */
@Slf4j
@RestController
@RequestMapping("/pay")
public class DirectlyPayController {

    private static final Integer directPfinancialPayment = 52011;

    private static final String OPERATE = "directlyPay";

    @Autowired
    VoucherFeignService voucherFeignService;


    @Autowired
    FibSealFeignService sealFeignService;


    @Autowired
    FibBudgetFeignService budgetFeignService;

    @Resource
    FibAccountService accountService;

    @Autowired
    RedisTemplate redisTemplate;

    /**
     * 支付信息表 需要凭证id，在data里面返回给我。
     * 预算表也在做。通过id查询金额的操作。
     */

    @PostMapping("/exec")
    public Result startDirectlyPay(@RequestBody @Validated DirectPfinancialPaymentVO vo) {
        exec(vo);
        return Result.ok().message("已交给银行处理。。。");
    }

    @Async
    public void exec(DirectPfinancialPaymentVO vo) {
        Result result = null;
        // 第一步 生成直接支付凭证
        try {
            result = directlyPayVoucher(vo);
            if (result.getCode() != 200) {
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.info("error:{}", e.getMessage());
        }
        log.info("result:{}", result);
//        log.info("result.getData:{}", result.getData());
        // 第二部 直接支付凭证盖章
        FibVoucherDetailsVO voucherDetailsVO = MyClassCastUtil.castObjectToVDVO(result.getData().get("data"));
        log.info("voucherDetailsVO:{}", voucherDetailsVO);
        if (voucherDetailsVO == null) {
            return;
        }
        try {
            result = directlyPaySeal(voucherDetailsVO, vo.getBgtId());
            if (result.getCode() != 200) {
                return;
            }
        } catch (Exception e) {
            log.info("error:{}", e.getMessage());
        }

        // 第三步 转账支付拨款
        try {
            result = directlyPayVoucher(vo);
            if (result.getCode() != 200) {
                return;
            }
        } catch (Exception e) {
            log.info("error:{}", e.getMessage());
        }

        // 第四步 生成回单
        // 第五步，回单盖章

        result.getData().get("data");
        // 第六步 生成入账通知书
        try {
            result = voucherNotice(vo, voucherDetailsVO.getVoucherId().toString());
            if (result.getCode() != 200) {
                return;
            }
        } catch (Exception e) {
            log.info("error:{}", e.getMessage());
        }

        // 第七步 入账通知书盖章保存
        try {
            result = directlyPayVoucher(vo);
            if (result.getCode() != 200) {
                return;
            }
        } catch (Exception e) {
            log.info("error:{}", e.getMessage());
        }
    }

    @PostMapping("/getStatus")
    public Result getStatus(@RequestParam("bgtId") Long bgtId) {

        Object status = redisTemplate.opsForValue().get(OPERATE + bgtId);
        if (status == null) {
            Result voucherResult = voucherFeignService.selectByBgtIdAndCode(directPfinancialPayment, bgtId);
            FibVoucherDetailsPO voucherDetails = (FibVoucherDetailsPO) voucherResult.getData().get("data");
            if (voucherDetails != null) {
                return Result.ok().message("该拨款项目已经生成过退款凭证了!已经操作过了!");
            }
            return Result.ok().message("正在生成凭证或还未申请生成凭证!");
        }

        String res = "";
        switch (status.toString()) {
            case "status_1":
                res = "正在给凭证盖章!";
                break;
            case "status_2":
                res = "正在准备拨款";
                break;
            case "status_3":
                res = "正在返回回单";
                break;
            case "status_4":
                res = "正在给回单盖章";
                break;
            case "status_5":
                res = "回单已经盖章保存";
            case "error":
                res = "状态异常, 请重新尝试";
                redisTemplate.delete(OPERATE + bgtId);
            default:
                break;
        }
        log.info("来自DirectlyController的消息:{}", res);
        return Result.ok().message(res).data("status", status);
    }

    /**
     * 第一步 生成直接支付凭证
     *
     * @param vo
     * @return
     */
    @PostMapping("/voucher")
    @LcnTransaction
    public Result directlyPayVoucher(@RequestBody @Validated DirectPfinancialPaymentVO vo) {

        Long bgtId = vo.getBgtId();
        //TODO 金额可能需要远程调用budget的接口查询,会更加合理。
        Result payVoucherResult = voucherFeignService.selectByBgtIdAndCode(directPfinancialPayment, bgtId);
        Map<String, Object> data = payVoucherResult.getData();
        FibVoucherDetailsPO voucherDetailsPO = (FibVoucherDetailsPO) data.get("data");

        // 因为是事务操作，所以如果生成了凭证，则事务已经操作结束了，或者正在进行中。
        if (voucherDetailsPO != null) {
            log.info("预算id为:{}的申请已经生成过凭证了!", bgtId);
            return Result.error().message("已经生成过凭证了");
        }

        try {
            Result result = voucherFeignService.addDirectPfinancialPayment(vo);
            log.info("生成凭证的result:{}", result);
            log.info("result的data:{}", result.getData());
            log.info("Integer.valueOf(200).equals(result.getCode()):{}", Integer.valueOf(200).equals(result.getCode()));
            if (Integer.valueOf(200).equals(result.getCode())) {
                log.info("生成直接支付凭证成功!");

                // 拿到远程调用返回过来的凭证信息
                Object o = result.getData().get("data");
                log.info("object:{}", o);
                FibVoucherDetailsPO payVoucherDetails = MyClassCastUtil.castObjectToVDPO(o);
                log.info("自定义方法转换之后:payVoucherDetails:{}", payVoucherDetails);
                // 转换成VO，作为参数传给下一个步骤 === 盖章
                FibVoucherDetailsVO voucherDetailsVO = new FibVoucherDetailsVO();
                voucherDetailsVO.setVoucherId(payVoucherDetails.getVoucherId());
                voucherDetailsVO.setSealId(3L);
                voucherDetailsVO.setVoucher_location_id(1);

                redisTemplate.opsForValue().set(OPERATE + bgtId, "status_1");
                return Result.ok().message("生成直接支付凭证成功!").data("voucher", voucherDetailsVO);
            }
        } catch (Exception e) {
            log.info("直接支付生成凭证失败!");
            redisTemplate.opsForValue().set(OPERATE + bgtId, "error");
            e.printStackTrace();
            throw DaoException.error();
        }
        redisTemplate.opsForValue().set(OPERATE + bgtId, "error");
        return Result.ok().message("远程调用生成凭证出错。。。");
    }

    /**
     * 第二步 给直接支付凭证 盖章
     *
     * @param vdVO
     * @param bgtId
     * @return
     */
    @PostMapping("/seal")
    public Result directlyPaySeal(FibVoucherDetailsVO vdVO, Long bgtId) {
        try {
            sealFeignService.stampTest(vdVO);
            redisTemplate.opsForValue().set(OPERATE + bgtId, "status_2");
            return Result.ok().message("盖章成功了");
        } catch (Exception e) {
            log.info("directlyPaySeal过程出错啦!");
            throw DaoException.error();
        }
    }

    /**
     * 第三步 递交银行转账
     *
     * @param vo
     * @return
     */
    @PostMapping("/transfer")
    @LcnTransaction
    public Result transfer(@RequestBody @Validated DirectPfinancialPaymentVO vo) {
        log.info("进入直接支付的转账环节");

        Long bgtId = vo.getBgtId();
        //TODO 通过bgtId查到金额, 有可能要通过其他信息查询到账户

//        Result res = budgetFeignService.getBgtById(bgtId);
//        FibBudgetVO budgetVO = (FibBudgetVO) res.getData().get("data");
//
//        BigDecimal bgtAmount = budgetVO.getBgtAmount();
        BigDecimal amount = new BigDecimal(vo.getMoneySmall());
//        if (!bgtAmount.equals(amount)) {
//            log.info("转账要求和预算申请数据不一致");
//        }

        if (vo.getMoneySmall() != null && amount.compareTo(new BigDecimal(0)) > 0) {
            log.info("amount:{}", amount);

            //付款账号
            String payerAccount = vo.getPayerAccount();
            //收款账户
            String payeeAccount = vo.getPayeeAccount();

            Long payerAccLong = Long.parseLong(payerAccount);
            Long payeeAccLong = Long.parseLong(payeeAccount);
            log.info("payerAccLong:{}, payeeAccLong:{}", payerAccLong, payeeAccLong);

            // 如果余额足够,就完成 转账 实操
            if (accountService.getBalanceById(Long.parseLong(vo.getPayerAccount())).compareTo(amount) > 0) {
                accountService.transferById(payerAccLong, payeeAccLong, amount);
                log.info("直接支付转账完成!");

                // TODO 生成一条支付记录。

                redisTemplate.opsForValue().set(OPERATE + bgtId, "status_3");
                return Result.ok().message("直接支付转账已完成!");
            }

            //TODO 如果余额不足就从其他账户()转钱过来，再转出去。
            //目前是如果金额不足就返回error
            return Result.error().message("余额不足!");
        }
        log.info("直接支付转账有问题!");
        redisTemplate.opsForValue().set(OPERATE + bgtId, "error");
        return Result.error().message("直接支付转账异常!");
    }

    //TODO 生成回单，盖章

    //这里直接就是入账通知书了

    /**
     * 第六步 生成直接支付入账通知书
     *
     * @param dfVO
     * @param voucherId
     * @return
     */
    @PostMapping("/voucherNotice")
    public Result voucherNotice(DirectPfinancialPaymentVO dfVO, String voucherId) {
        Long bgtId = dfVO.getBgtId();
        FinancialDirectPaymentEntryNoticeVO fpVO = new FinancialDirectPaymentEntryNoticeVO();
        fpVO.setCombinedLowercase(dfVO.getMoneySmall());
        //支付凭证
        fpVO.setPaymentVoucher(voucherId);


        Result feignResult = budgetFeignService.getBgtById(bgtId);
        FibBudgetVO bgtVO = (FibBudgetVO) feignResult.getData().get("data");

        // 预算单位
        fpVO.setBudgetUnit(bgtVO.getBgtDepartment().toString());
        //经办人
        fpVO.setAgent("银行");
        fpVO.setBgtId(bgtId);
        fpVO.setAccount(dfVO.getPayeeAccount());

        Result result = voucherFeignService.addFinancialDirectPaymentEntryNotice(fpVO);
        // 生成的凭证放在这个data里面了
        Object data = result.getData().get("data");

        redisTemplate.opsForValue().set(OPERATE + bgtId, "status_4");
        return Result.ok().message("生成通知书成功！递交给部门盖章保存").data("data", data);
    }

    /**
     * 第七步--- 最后一步 盖章保存
     *
     * @param vnVO
     * @param bgtId
     * @return
     */
    @PostMapping("/sealres")
    public Result sealRes(FibVoucherDetailsVO vnVO, Long bgtId) {
        try {
            sealFeignService.stampTest(vnVO);
            redisTemplate.opsForValue().set(OPERATE + bgtId, "status_5");
            return Result.ok().message("盖章成功了");
        } catch (Exception e) {
            log.info("directlyPayResSeal过程出错啦!");
            throw DaoException.error();
        }
    }

}
